<?php
require_once("Entity.php");

/**		
 * @author Matteo Vigoni <mattevigo@gmail.com>																				
 * @package DBEntity
 * 
 * @version 0.2 - last update 08 nov 2009 - 16:00
 * 
 * This class is the core of the Database Abstraction Layer.
 * An istance of DBEntity rappresent a record of a generic table on the DB. You can create 
 * an object with DBEntity and save it in the database, errors will be processed from the
 * DBMS, it's very important know what you are doing and the struct of the DB.
 * 
 * The best way to use DBEntity is extend it for made object with your own functionality,
 * take a look at the Session and User class in the core directory for an example.	
 *
 */
class DBEntity implements Entity
{
	/**
	 * @var $primary_key field name for primary key 
	 * @var $id primary key value
	 * 
	 * @var $ext_id TODO
	 */
	private $id = null;
	private $ext_id = null;
	
	private $values = Array();
	private $changes = Array();
	public $is_new = true;
	
	public $db_wrapper;
	public $table = null;	# the name of the table that this DBEntity rappresent
	public $primary_key;
	
	// commit
	private $commit_query_update = "UPDATE ";
	private $commit_query_set = "SET ";
	private $commit_query_where = "WHERE ";
	
	/**
	 * Constructor
	 * 
	 * @param DB $db_wrapper the DB object that rappresent this database
	 * @param string $table the table name for this entity
	 * @param string $primary_key the field name of the primary key
	 * 
	 * @throws DBException for problem with DBMS
	 * 
	 * @todo implementazione di chiavi primarie multiple da realizzare con due array $pk e $kv che contengono
	 * 			i nomi e i valori delle chiavi primarie.
	 */
	function __construct(DB $db_wrapper, $table, $primary_key, $id=null)
	{
		$this->db_wrapper = $db_wrapper;
		$this->table = $table;
		$this->primary_key = $primary_key;
		
		if(isset($id))
		{
			$this->id = $id;
			
			$mysql_query = 	"SELECT * " .
				"FROM $table " .
				"WHERE $primary_key=";
			
			if( is_string( $this->id ) )
			{
				$mysql_query .= "'$id'";
			}
			else
			{
				$mysql_query .= $id;
			}
			
			$result = $this->db_wrapper->query($mysql_query);
			$data_array = mysql_fetch_assoc($result);
			
			if(!$data_array) throw new EntityException("The '".$id."' doesn't exist in ".$db_wrapper->name().".".$table);
			
			//var_dump($data_array); # debug
			foreach($data_array as $key => $value)
			{
				$this->values[$key] = $value;
			}
			
			$this->is_new = false;
		}
	}
	
	/**
	 * Get the primary key value for this DBEntity
	 * 
	 * @return string the id for this DBEntity
	 */
	public function getId()
	{
		return $this->id;
	}
	
	/**
	 * Get the XML rappresentation for this DBEntity
	 * 
	 * @return string XML rappresentation for this DBEntity
	 */
	public function getXML()
	{
		$xml = "<$this->table>";
		
		foreach($this->values as $key => $value){
			$xml .= "<$key>$value</$key>";
		}
		
		$xml .= "</$this->table>";
		
		return $xml;
	}
	
	/**
	 * Get the JSON rappresentation for this DBEntity
	 * 
	 * @return string JSON rappresentation for this DBEntity
	 */
	public function getJSON()
	{
		$json = "{\n";
		$nelem = count($this->values);
		$i = 0;
		
		foreach($this->values as $key => $value)
		{
			$json .= "\t\"$key\":\"$value\"";
			$i++;
			if($i<$nelem) $json.= ",\n";
		}
		
		return $json."\n}";
	}
	
	/**
	 * Get the value for the generic $index field name
	 * 
	 * @param string $index the field name
	 */
	public function get($index)
	{
		if(!isset($this->values[$index]))
			throw new EntityException("No value found for '".$index."'");
		
		return $this->values[$index];
	}
	
	/**
	 * Set/Modify a record value. This modify chenge only the object and not the datbase value that
	 * the object rappresent. To apply change to the database too you need to execute $this->commit()
	 * 
	 * @param string $index the field name 
	 * @param string $value the field values 
	 */
	public function set($index, $value)
	{	
		$this->values[$index] = $value;
		$this->changes[$index] = true;
	}
	
	/**
	 * Apply all the changes to the backened database.
	 */
	public function commit()
	{	
		$toSet = $this->commit_query_set;
		$first = TRUE; # variabile usata per posizionare le virgole
		
		foreach($this->changes as $key => $val){ # iterazione all'interno dell'array
			
			if($val){
				
				if($first) {					#
					$first = FALSE;				# Corretto posizionamento della virgola
				} else $toSet = $toSet . ", ";	#
				
				# viene aggiornato solo il valore cambiato
				$toSet = $toSet . "`" . $key . "`='" . $this->values[$key] . "'";
			}
			
		}
		
		$toSet = $toSet . " ";
		//echo $toSet . "<br />";	# stampa di verifica
		
		$query = $this->commit_query_update . $this->table . " "
			. $toSet 
			. $this->commit_query_where . "`{$this->table}`.`{$this->primary_key}`='" . $this->getId() 
			. "' LIMIT 1";
		
		//echo $query . "<br />";	# stampa di verifica
		try{
			$this->db_wrapper->query($query);
		} catch (DBException $e){ 
			throw new EntityException($e->getMessage());
		}
		
		$this->reset();
	}
	
	/**
	 * Insert a new record on the database table that this class rappresent.
	 * 
	 * @param string $id the primary key value, if this is null will assigned one from the DB, if AUTO_INCREMENT
	 * 			is present.
	 * 
	 * @trow EntityException se la chiave primaria e' gie' definita oppure se e' occorso un errore
	 * 							durante la scrittura nel database.
	 */
	public function store($id=null)
	{	
		$mysql_query_insert = "INSERT INTO `{$this->db_wrapper->name()}`.`$this->table` (";
		$mysql_query_values = ")VALUES (";
		
		//echo "sid: ".$id . "<br />";
		
		$mysql_query_insert .= $this->primary_key . ", ";
		if(is_null($id)) $mysql_query_values .= "NULL, ";
		else {
			if(is_string($id)) $mysql_query_values .= "'$id', ";
			else $mysql_query_values .= "$id, ";
		}
		
		$size = count($this->values);
		
		$i = 1;
		foreach($this->values as $key => $value){
			
			$mysql_query_insert .= $key;
			
			if(is_string($value)) $mysql_query_values .= "'$value'";
			else $mysql_query_values .= $value;
			
			if($i < $size){
				$mysql_query_insert .= ", ";
				$mysql_query_values .= ", ";
			}
			$i++;
		}
		
		$mysql_query = $mysql_query_insert . $mysql_query_values . ");";
		//echo $mysql_query;
		
		try{
			//scrivo i cambiamenti sul database
			$result = $this->db_wrapper->query($mysql_query);
			if(is_null($id)) $this->id = mysql_insert_id();
			else $this->id = $id;
			//echo "ID ".$this->id."<br />";
			
		} catch (DBException $e){
			throw new EntityException($e->getMessage());
		}
		
		//resetto tutti i valori ausiliari
		$this->reset();
	}
	
	/**
	 * Delete this record from database
	 * 
	 * @throws DBException
	 */
	public function cancel()
	{	
		$mysql_query = "DELETE FROM `$this->table` WHERE `$this->table`.`$this->primary_key` = $this->id LIMIT 1";
		$this->db_wrapper->query($mysql_query);
	}
	
	/**
	 * Reset all the aux vars
	 */
	private function reset()
	{	
		$this->commit_query_set = "SET ";
		$this->commit_query_update = "UPDATE ";
		$this->commit_query_where = "WHERE ";
		
		foreach ( $this->changes as $key => $value ) {
			$this->changes[$key] = false;
		}
	}
	
	/**
	 * Count the number of record on this table
	 * 
	 * @param DB $db_wrapper the DB class for this database
	 * @param string $table_name
	 * 
	 * @return the number of record in $table_name
	 */
	public static function count(DB $db_wrapper, $table_name)
	{	
		$mysql_query = "SELECT count(*) FROM ".$table_name." WHERE 1";
		$data = mysql_fetch_row($db_wrapper->query($mysql_query));
		
		return $data[0];
	}
	
	/**
	 * Return the DBEntity that rappresent the record identified from the value of $id
	 * of its $primary_key
	 * 
	 * @param DB $db_wrapper the DB class for this database
	 * @param string $table_name the name of the table
	 * @param string $primary_key the field name of the primary key
	 * @param string $id the primary key value
	 * 
	 * @return the DBEntity that rappresent the record
	 */
	public static function getFromId(DB $db_wrapper, $table_name, $primary_key, $id)
	{	
		return new DBEntity($db_wrapper, $table_name, $primary_key, $id);
	}
	
	/**
	 * Return the XML rappresentation of the record identified from the value of $id of its
	 * $primary_key.
	 * 
	 * @param DB $db_wrapper the DB class for this database
	 * @param string $table_name the name of the table
	 * @param string $primary_key the field name of the primary key
	 * @param string $id the primary key value
	 * 
	 * @return a string that contains the XML rappresentation of the record
	 */
	public static function getXMLFromId(DB $db_wrapper, $table_name, $prymary_key ,$id)
	{	
		$mysql_query = 	"SELECT * " .
			"FROM $table_name " .
			"WHERE $primary_key=$id ";
		
		$xml = "<$table_name>";
		
		try{			
			$data = mysql_fetch_assoc($db_wrapper->query($mysql_query));
			
			foreach($data as $key => $value){
				$xml .= "<$key>$value</$key>";
			}
		}catch (DBException $e){
			throw new EntityException($e->getMessage());
		}
		
		$xml .= "</$table_name>";
		
		return $xml;
	}
	
	/**
	 * Return the JSON rappresentation of the record identified from the value of $id of its
	 * $primary_key.
	 * 
	 * @param DB $db_wrapper the DB class for this database
	 * @param string $table_name the name of the table
	 * @param string $primary_key the field name of the primary key
	 * @param string $id the primary key value
	 * 
	 * @return a string that contains the JSON rappresentation of the record
	 */
	public static function getJSONFromId(DB $db_wrapper, $table_name, $primary_key, $id)
	{
		$mysql_query = 	"SELECT * " .
			"FROM $table_name " .
			"WHERE $primary_key=$id ";
		
		$json = "{\n";
		
		try{			
			$data = mysql_fetch_assoc($db_wrapper->query($mysql_query));
			
			$nelem = count($data);
			$i = 0;
			
			foreach($data as $key => $value){
				$json .= "\t\"$key\":\"$value\"";
				$i++;
				if($i<$nelem) $json.= ",\n";
			}
		}catch (DBException $e){
			throw new EntityException($e->getMessage());
		}
		
		$json .= "\n}";
		
		return $json;
	}
}
?>
